package com.atguigu.gulimall.product.aspectj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.common.annotation.GulimallCache;
import com.atguigu.common.constant.RedisConst;
import com.atguigu.gulimall.product.entity.CategoryEntity;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.util.concurrent.TimeUnit;


/**
 * @Author LiDeJie
 * @Create 2024/1/10 20:19
 * @Version 1.0
 */
@Component
@Aspect
public class CataLogJsonAspect {
    
    @Autowired
    RedisTemplate redisTemplate;
    
    @Autowired
    RedissonClient redissonClient;
    
    
    @Around(value = "@annotation(com.atguigu.common.annotation.GulimallCache)")
    public Object cacheAroundAdvice(ProceedingJoinPoint joinPoint) throws InstantiationException, IllegalAccessException {
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        GulimallCache gulimallCache = signature.getMethod().getDeclaredAnnotation(GulimallCache.class);
        String prefix = gulimallCache.prefix();
        String suffix = gulimallCache.suffix();
        Class returnType = signature.getReturnType();
        String cacheKey = cacheKey(prefix, suffix);
        Object cacheData = redisTemplate.opsForValue().get(cacheKey);
        if (cacheData != null) {
            return cacheData;
        }
        String lockKey = getLockKey(prefix);
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            cacheData = joinPoint.proceed();
            if (cacheData != null) {
                redisTemplate.opsForValue().set(cacheKey, cacheData, RedisConst.CATALOG_ONE_LEVEL_TIME, TimeUnit.SECONDS);
            }else {
                cacheData = returnType.newInstance();
                redisTemplate.opsForValue().set(cacheKey, cacheData, RedisConst.CATALOG1_NO_FOUNT_TIME, TimeUnit.SECONDS);
            }
            return cacheData;
        } catch (Throwable e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        
        return null;
    }
    
    private Object getObject(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        //获取在方法上面的注解
        GulimallCache gulimallCache = signature.getMethod().getDeclaredAnnotation(GulimallCache.class);
        Class returnType = signature.getReturnType();
        //获取目标方法参数
        //Object[] methodParameters = proceedingJoinPoint.getArgs();
        String prefix = gulimallCache.prefix();
        String suffix = gulimallCache.suffix();
        String cacheKey = cacheKey(prefix, suffix);
        Object obj = cacheHit(cacheKey, returnType);
        if (obj != null) {
            return obj;
        }
        String lockKey = getLockKey(prefix);
        //进行加锁
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            Object objData = proceedingJoinPoint.proceed();
            if (objData == null) {
                objData = returnType.newInstance();
                redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(objData), RedisConst.CATALOG1_NO_FOUNT_TIME, TimeUnit.SECONDS);
            }else {
                redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(objData), RedisConst.CATALOG_ONE_LEVEL_TIME, TimeUnit.SECONDS);
            }
            return objData;
        } catch (Throwable e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return null;
    }
    
    private static String getLockKey(String prefix) {
        String lockKey = prefix + RedisConst.CATALOG1_LOCK;
        return lockKey;
    }
    
    /**
     * 从缓存中获取目标数据并向上转型
     */
    private Object cacheHit(String cacheKey, Class returnType) {
        String cacheData = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cacheData != null) {
            Object obj = JSON.parseObject(cacheData, returnType);
            return obj;
        }
        return null;
    }
    
    /**
     * 获取缓存Key
     */
    private static String cacheKey(String prefix, String suffix) {
        return prefix + RedisConst.CATALOG_ONE_LEVEL + suffix;
    }
    
    
}
